﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Web.UI;
using NetDon.Utilities;
using SD.LLBLGen.Pro.ORMSupportClasses;


namespace NetDon.OA.Business
{
    public abstract class CategoryHierarchyService<T> : HierarchyCachableService<T>
         where T : class, IHierarchyData
    {
        protected CategoryHierarchyService(string cacheKey)
            : base(cacheKey)
        {
        }

        protected abstract IEnumerable<T> OnLoadRootNodes(object category, ITransaction transaction);

        protected abstract IEnumerable<T> OnLoadRetailNodes(object category, ITransaction transaction);

        protected abstract object GetCategory(T data);

        protected virtual string GenerateCategoryCacheKey(object category)
        {
            return CacheKey + "_Category_" + category;
        }

        protected IEnumerable<T> GetRootNodes(object category)
        {
            return GetRootNodes(category, null);
        }
        protected IEnumerable<T> GetRetailNodes(object applicationId)
        {
            return GetRetailNodes(applicationId, null);
        }

        protected virtual IEnumerable<T> GetRetailNodes(object applicationId, ITransaction transaction)
        {
            if (applicationId == null)
            {
                throw new ArgumentNullException("applicationId");
            }
            string categoryCacheKey = GenerateCategoryCacheKey(applicationId);
            var keys = GetCache<ArrayList>(categoryCacheKey);
            if(keys==null)
            {
                var retailNodes = OnLoadRetailNodes(applicationId, transaction);
                if(retailNodes==null)
                {
                    SetCache(categoryCacheKey,null);
                    return null;
                }
                keys = new ArrayList();
                foreach (T data in retailNodes)
                {
                    object key = GetDataKey(data);
                    SetCache(GenerateCacheKey(key),data);
                    keys.Add(key);
                }
                SetCache(categoryCacheKey,keys);
                return retailNodes;
            }
            var listData = new List<T>();
            foreach (object key in keys)
            {
                listData.Add(GetData(key,transaction));
            }
            return listData;
        }

        protected virtual IEnumerable<T> GetRootNodes(object category, ITransaction transaction)
        {
            if (category == null)
            {
                throw new ArgumentNullException("category");
            }
            string categoryCacheKey = GenerateCategoryCacheKey(category);
            var keys = GetCache<ArrayList>(categoryCacheKey);
            if (keys == null)
            {
                var rootNodes = OnLoadRootNodes(category, transaction);
                if (rootNodes == null)
                {
                    SetCache(categoryCacheKey, null);
                    return null;
                }
                keys = new ArrayList();
                foreach (T data in rootNodes)
                {
                    object key = GetDataKey(data);
                    SetCache(GenerateCacheKey(key), data);
                    keys.Add(key);
                }
                SetCache(categoryCacheKey, keys);
                return rootNodes;
            }
            var listData = new List<T>();
            foreach (object key in keys)
            {
                listData.Add(GetData(key, transaction));
            }
            return listData;
        }

        protected override void OnAddDataComplete(object key, T data)
        {
            base.OnAddDataComplete(key, data);
            AddToRootCache(data);
        }

        protected override void OnRemoveDataComplete(object key, T data)
        {
            base.OnRemoveDataComplete(key, data);
            RemoveFromRootCache(data);
        }

        protected override bool ModifyData(object key, T data, ITransaction transaction)
        {
            var oldData = GetData(key, transaction);
            if (oldData == null)
            {
                return false;
            }
            if (base.ModifyData(key, data, transaction))
            {
                if (!Equals(GetParentKey(oldData), GetParentKey(data)))
                {
                    RemoveFromRootCache(oldData);
                    AddToRootCache(data);
                }
                return true;
            }
            return false;
        }

        private void AddToRootCache(T data)
        {
            var parentKey = GetParentKey(data);
            if (!MiscUtilities.IsNull(parentKey))
            {
                return;
            }
            string categoryCacheKey = GenerateCategoryCacheKey(GetCategory(data));
            var keys = GetCache<ArrayList>(categoryCacheKey);
            if (keys == null)
            {
                return;
            }
            var key = GetDataKey(data);
            if (!keys.Contains(key))
            {
                keys.Add(key);
            }
            SetCache(categoryCacheKey, keys);
        }

        private void RemoveFromRootCache(T data)
        {
            var parentKey = GetParentKey(data);
            if (!MiscUtilities.IsNull(parentKey))
            {
                return;
            }
            string categoryCacheKey = GenerateCategoryCacheKey(GetCategory(data));
            var keys = GetCache<ArrayList>(categoryCacheKey);
            if (keys == null)
            {
                return;
            }
            var key = GetDataKey(data);
            if (keys.Contains(key))
            {
                keys.Remove(key);
            }
            SetCache(categoryCacheKey, keys);
        }

        protected virtual bool Sortable
        {
            get { return false; }
        }

        protected virtual int GetSortIndex(T data)
        {
            return 0;
        }

        protected virtual bool ExchangeIndex(T x, T y)
        {
            return true;
        }

        protected bool MoveupData(T data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }
            object parentKey = GetParentKey(data);
            IEnumerable<T> items = parentKey != null ? GetChildNodes(parentKey) : GetRootNodes(GetCategory(data));
            int maxIndex = -1;
            T previous = null;
            int currentIndex = GetSortIndex(data);
            foreach (T e in items)
            {
                int i = GetSortIndex(e);
                if (currentIndex > i && i > maxIndex)
                {
                    maxIndex = i;
                    previous = e;
                }
            }
            if (previous == null)
            {
                return false;
            }
            if (ExchangeIndex(previous, data))
            {
                SetCache(GenerateCacheKey(GetDataKey(data)), data);
                SetCache(GenerateCacheKey(GetDataKey(previous)), previous);
                return true;
            }
            return false;
        }

        protected bool MovedownData(T data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }
            object parentKey = GetParentKey(data);
            IEnumerable<T> items = parentKey != null ? GetChildNodes(parentKey) : GetRootNodes(GetCategory(data));
            int minIndex = int.MaxValue;
            T next = null;
            int currentIndex = GetSortIndex(data);
            foreach (T e in items)
            {
                int i = GetSortIndex(e);
                if (currentIndex < i && i < minIndex)
                {
                    minIndex = i;
                    next = e;
                }
            }
            if (next == null)
            {
                return false;
            }
            if (ExchangeIndex(next, data))
            {
                SetCache(GenerateCacheKey(GetDataKey(data)), data);
                SetCache(GenerateCacheKey(GetDataKey(next)), next);
                return true;
            }
            return false;
        }
    }
}
